import { Injectable, BadRequestException } from '@nestjs/common';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Post, ResourceFile } from '../entities';
import { CrudRequest } from '@nestjsx/crud';
import { DeepPartial, Repository } from 'typeorm';
import { FileUtils, VideoUtils } from '@/utils';
import { FileStoragePath } from '@/enum';
import * as path from 'path';
import * as _ from 'lodash';
import { plainToClass } from 'class-transformer';

@Injectable()
export class PostsService extends TypeOrmCrudService<Post> {
  constructor(
    @InjectRepository(Post) repo,
    @InjectRepository(ResourceFile)
    private readonly fileRepo: Repository<ResourceFile>,
  ) {
    super(repo);
  }

  async createOne(req: CrudRequest, dto: DeepPartial<Post>): Promise<Post> {
    if (dto.video) {
      const { video, thumb } = await this.saveVideoFile(dto.video);
      dto.video = video;
      if (!dto.coverImage) {
        dto.coverImage = thumb.fullpath;
      }
    }
    return super.createOne(req, dto);
  }

  async updateOne(req: CrudRequest, dto: DeepPartial<Post>): Promise<Post> {
    if (dto.video && !dto.video.id) {
      const { video, thumb } = await this.saveVideoFile(dto.video);
      dto.video = video;
      if (!dto.coverImage) {
        dto.coverImage = thumb.fullpath;
      }
    }
    return super.updateOne(req, dto);
  }

  async replaceOne(req: CrudRequest, dto: DeepPartial<Post>): Promise<Post> {
    if (dto.video && !dto.video.id) {
      const { video, thumb } = await this.saveVideoFile(dto.video);
      dto.video = video;
      if (!dto.coverImage) {
        dto.coverImage = thumb.fullpath;
      }
    }
    return super.replaceOne(req, dto);
  }

  /**
   * Soft delete one entity.
   *
   * Override from the `Crud` `deleteOne` function to soft delete.
   * @param req `CrudRequest` from `Crud`
   */
  async deleteOne(req: CrudRequest): Promise<void | Post> {
    const { returnDeleted } = req.options.routes.deleteOneBase;
    const found = await this.getOneOrFail(req, returnDeleted);
    const toReturn = returnDeleted
      ? plainToClass(this.entityType, { ...found })
      : undefined;
    await this.repo.softRemove(found);
    return toReturn;
  }

  private async saveVideoFile(file: DeepPartial<ResourceFile>) {
    const video = await this.saveFile(file);
    const thumb = await VideoUtils.getThumbs(
      path.resolve(process.cwd(), video.path),
    );
    return { video, thumb };
  }

  private async saveFile(file: DeepPartial<ResourceFile>) {
    file.sha256 = await FileUtils.getHashOfFile(file.path);
    let path = '';
    switch (FileUtils.getTypeFromMimetype(file.mimetype)) {
      case 'image':
        path = FileStoragePath.UPLOAD_IMAGES;
        break;
      case 'video':
        path = FileStoragePath.UPLOAD_VIDEOS;
        break;
      default:
        throw new BadRequestException(
          `Unexpected file type "${file.mimetype}" of file ${file.originalname}`,
        );
    }
    path += file.filename;
    await FileUtils.moveFile(file.path, path);
    file.path = path;
    return await this.fileRepo.save(file);
  }
}
